#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/md5.h>
#include <glib.h>
#include <dlfcn.h>
#include <time.h>

#include "dict.h"
#include "date.h"
#include "encrypt.h"
#include "kylin-ukey.h"
#include "kylin-activation.h"
#include "kylin-verify.h"
#include "misc.h"

#include "kylin-license.h"

#include "type.h"

#include "ukey-macro.h"

#ifdef ENABLE_FTKEY
#include <Dongle_API.h>
#include <Dongle_CORE.h>


/*
定义 飞天诚信的 RockeyARM 锁的普通用户空间大小、偏移量、root用户空间偏移量
*/
#define	FEITIAN_USER_SPACE_SIZE		4096
#define	FEITIAN_USER_SPACE_OFFSET	0
#define	FEITIAN_ADMIN_SPACE_OFFSET	4096


extern char* _kylin_activation_get_register_number(const char* serial_no, int* result);

static int _ftkey_used_register_count(int space_size);

static int USER_SPACE = FEITIAN_USER_SPACE_SIZE;
static int USER_SPACE_OFFSET = FEITIAN_USER_SPACE_OFFSET;
//static int ADMIN_SPACE = 1024;
static int ADMIN_SPACE_OFFSET = FEITIAN_ADMIN_SPACE_OFFSET;

#define CODE_LENGTH 8
/*
存储区容量大小
*/
#define BUF_SIZE 8192
#define LINE_SIZE 256
#define MAX_LENGTH 4096
#define KEY_FILE_SIZE 512

#define LICENSE_GROUP "license"
#define SPACE_ALIGNMENT 1000

#define ENCRYPTED_NUMBER_LENGTH 20

/*
定义 飞天诚信的 RockeyARM 锁的路径、md5信息
*/
#if defined __x86_64 || defined __x86_64__ || defined __amd64 || defined __amd64__
#define	FTKEY_SHARED_LIBRARY_PATH		"/usr/lib/x86_64-linux-gnu/libRockeyARM.so.0.3"
#define	FTKEY_SHARED_LIBRARY_MD5SUM		"068c8deda08eaf0bda5d3383d6a425b5"
#define	FTKEY_SHARED_LIBRARY_SHA1SUM		"5f5399d1feb05015fdfb120e8e49dda8d0c2d485"
#define	FTKEY_SHARED_LIBRARY_SHA256SUM		"4e55b42f7a7eefb505006dfe05d9d8a73705f70d0717fe4b0d4e9d551c557c96"
#endif

#if defined __i386 || defined __i386__ || defined __i686 || defined __i686__
#define	FTKEY_SHARED_LIBRARY_PATH		"/usr/lib/i386-linux-gnu/libRockeyARM.so.0.3"
#define	FTKEY_SHARED_LIBRARY_MD5SUM		"ae3b55ae7b401fb1713b7009e5073db2"
#define	FTKEY_SHARED_LIBRARY_SHA1SUM		"9aa7f4f7e2e59b33f06cc324506452b34de5e8d7"
#define	FTKEY_SHARED_LIBRARY_SHA256SUM		"949967eaafb575ecd0ec533628866e0afd96979f58182744ced104169d2d116d"
#endif

#ifdef __aarch64__
#define	FTKEY_SHARED_LIBRARY_PATH		"/usr/lib/aarch64-linux-gnu/libRockeyARM.so.0.3"
#define	FTKEY_SHARED_LIBRARY_MD5SUM		"697ae584a7f79e5e811236e2a793d5f0"
#define	FTKEY_SHARED_LIBRARY_SHA1SUM		"54b489f83937afb1fcbd29dff68fd1bac01149f1"
#define	FTKEY_SHARED_LIBRARY_SHA256SUM		"d1c3acb2d5c30df46677e44e1c275564067b2fd6adcbc63351a9f9e5cabc05a9"
#endif



#ifdef __mips__
  #ifdef __LP64__
    #define FTKEY_SHARED_LIBRARY_PATH "/usr/lib/mips64el-linux-gnuabi64/libRockeyARM.so"
  #endif
#endif


//global variables in this file
static WORD ftkey_index = 0;
static unsigned char ftkey_data[BUF_SIZE];
static char dump_message[BUF_SIZE];
static GKeyFile* ftkey_license_keyfile = NULL;
static int has_already_get = 0;

/*
保存设备信息
*/
static DONGLE_INFO	gDongleInfo[32];
/*
保存设备打开时句柄
*/
static DONGLE_HANDLE	gDongleHandle[32];
/*
设备数
*/
static int		gDongleCount = 0;

/*
 * ftkey_get_hid_buf, get hid of feitian
 * return NO_ERROR if succeed, or error number if not
 */
//static int ftkey_get_hid_buf(char* buf, int size);

/*
 * ftkey_init, init feitian environment
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_init();

/*
 * ftkey_check_library, check if feitian library is valid
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_check_library();

/*
定义 飞天诚信的RockeyARM 函数指针
*/
// 枚举加密锁
DWORD (*my_FtkeyEnum)(DONGLE_INFO *pDongleInfo, int *pCount);
// 打开加密锁
DWORD (*my_FtkeyOpen)(DONGLE_HANDLE *phDongle, int nIndex);
// 关闭加密锁
DWORD (*my_FtkeyClose)(DONGLE_HANDLE hDongle);
// 读数据
DWORD (*my_FtkeyReadData)(DONGLE_HANDLE hDongle, int nOffset, BYTE *pData, int nDataLen);
// 写数据
DWORD (*my_FtkeyWriteData)(DONGLE_HANDLE hDongle, int nOffset, BYTE *pData, int nDataLen);
// 种子码
DWORD (*my_FtkeySeed)(DONGLE_HANDLE hDongle, BYTE *pSend, int nSendLen, BYTE *pOutData);


/*
 * ftkey_find, find feitian
 * return NO_ERROR if feitian is inserted, or error number if not
 */
int ftkey_find(DWORD* count)
{
	DWORD ret;

	/*
	扫描当前USB接口中是否存在 飞天诚信的锁
	gDongleInfo : 返回所有设备的句柄
	count       : 返回设备数量
	*/
	ret = my_FtkeyEnum(gDongleInfo, &gDongleCount);
	if(ret != DONGLE_SUCCESS || gDongleCount <= 0) {
		// 没有设备
		return NO_UKEY_FOUND;
	}

	// 打开第一个设备
	ret = my_FtkeyOpen( gDongleHandle + ftkey_index , ftkey_index );
	if( ret != DONGLE_SUCCESS ) {
		// 无法打开设备
		return NO_UKEY_FOUND;
	}

	*count = gDongleCount;

	return NO_ERROR;

}


/*
 * ftkey_get_hid, get feitian hid
 * return feitian hid string on success, or NULL if failed
 */
char* ftkey_get_hid()
{
	DWORD ret;
	//DWORD hid;
	DWORD count;

	char buf[BUF_SIZE];

	ret = ftkey_find(&count);
	if(ret != NO_ERROR)
		return NULL;

	memset(buf, 0, BUF_SIZE);
	if( ftkey_index < count && ftkey_index >= 0 ) {
		for(int i=0; i<8; i++) {
			sprintf(buf+2*i, "%02u", gDongleInfo[ftkey_index].m_HID[i]);
		}

		return strdup(buf);
	}
	return NULL;
}

/*
 * ftkey_get_hid_buf, get hid of feitian
 * return NO_ERROR if succeed, or error number if not
 */
/*
static int ftkey_get_hid_buf(char* buf, int size)
{
	DWORD ret;
	DWORD hid;
	DWORD count;

	ret = ftkey_find(&count);
	if(ret != NO_ERROR)
		return ret;

	memset(buf, 0, size);
	if( ftkey_index < count && ftkey_index >= 0 ) {
		for(int i=0; i<8; i++) {
			sprintf(buf+2*i, "%02u", gDongleInfo[ftkey_index].m_HID[i]);
		}
		return NO_ERROR;
	}
	return UKEY_GET_HID_ERROR;
}
*/

/*
 * ftkey_get_data, get data in feitian
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_get_data(int force)
{
	DWORD ret;

	if(!force && has_already_get)
		return NO_ERROR;

	ret = ftkey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_FtkeyReadData(gDongleHandle[ftkey_index], USER_SPACE_OFFSET, ftkey_data, USER_SPACE + KEY_FILE_SIZE);
	if(ret != DONGLE_SUCCESS)
	{
		has_already_get = 0;
		return UKEY_GET_DATA_ERROR;
	}

	has_already_get = 1;

	return NO_ERROR;
}

/*
 * ftkey_get_data_force, force to get data in feitian
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_get_data_force()
{
	DWORD ret;
	DWORD count;
	
	ret = ftkey_find(&count);
	if(ret != NO_ERROR)
		return ret;

	// delete vikey_login

	return ftkey_get_data(1);
}


/*
 * ftkey_write_data, write data to feitian key
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_write_data(unsigned char* buf, size_t offset, size_t length)
{
	DWORD ret;

	ret = ftkey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_FtkeyWriteData(gDongleHandle[ftkey_index], offset, buf, length);
	if(DONGLE_SUCCESS != ret)
		return UKEY_WRITE_DATA_ERROR;

	return NO_ERROR;
}

/*
 * ftkey_seed, run seed
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_seed()
{
	DWORD ret;
	char* seed = "12345678";
	int seedLen = strlen(seed);
	unsigned char outBuf[BUF_SIZE] = {0};

	ret = ftkey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = my_FtkeySeed(gDongleHandle[ftkey_index], (unsigned char*)seed, seedLen, outBuf);
	if(DONGLE_SUCCESS != ret) {
		return UKEY_MODULE_NO_MORE_COUNT;
	}

	return NO_ERROR;
}

/*
 * ftkey_add_register_code, add a register code into feitian key
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_add_register_code(const char* register_code, int* _offset)
{
	DWORD ret;
	//unsigned short module_count = 0;
	unsigned short aligned_space = 0;
	int offset = -1;
	//unsigned short is_zero = 0;
	//unsigned short can_reduce = 0;
	char buf[BUF_SIZE];

	if(!register_code_is_valid(register_code))
		return INVALID_REGISTER_CODE;

	ret = ftkey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = ftkey_get_data_force();
	if(ret != NO_ERROR)
		return ret;

	ret = kylin_get_license(&ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);
	if(ret != NO_ERROR)
		return ret;

	// delete vikey_get_module

	memset(buf, 0, sizeof(buf));
	memcpy(buf, ftkey_data, USER_SPACE);

//	ret = vikey_find_register_code(register_code, buf, sizeof(buf));
//	if(ret)
//		return REGISTER_CODE_ALREADY_IN_UKEY_SPACE;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
	offset = kylin_check_register_code_space_validation(buf, USER_SPACE);

	if(offset >= aligned_space)
		return UKEY_SPACE_FULL;

	// delete vikey_check_module
	/*
	if(is_zero)
		return UKEY_MODULE_IS_ZERO;
	if(!can_reduce)
		return UKEY_MODULE_CANNOT_REDUCE;
	*/

	ret = ftkey_write_data((unsigned char*)register_code, offset, ENCRYPTED_NUMBER_LENGTH);
	if(ret != NO_ERROR)
		return ret;

	if(_offset)
		*_offset = offset;

	return NO_ERROR;
}

/*
 * ftkey_space_is_full, check if feitian key space is full
 * return 1 if full, or 0 if not
 */
static int ftkey_space_is_full(const char* buf, const size_t size)
{
	int aligned_space;
	int offset = 0;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
	offset = kylin_check_register_code_space_validation(buf, size);

	if(offset < aligned_space)
		return 0;

	return 1;
}


/*
 * ftkey_load_library, load feitian key library
 * return NO_ERROR if succeed, or error number if not
 */
int ftkey_load_library()
{
	void* handle;
	char* error;
	int ret;

	ret = ftkey_check_library();
	if(!ret)
		return UKEY_LIBRARY_LOAD_ERROR;

	// 加载 /usr/lib/{arch}-linux-gnu/libRockeyARM.so
	handle = dlopen(FTKEY_SHARED_LIBRARY_PATH, RTLD_NOW);
	if(!handle)
		return UKEY_LIBRARY_LOAD_ERROR;

	// 枚举加密锁
	my_FtkeyEnum = dlsym(handle, "Dongle_Enum");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 打开加密锁
	my_FtkeyOpen = dlsym(handle, "Dongle_Open");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 关闭加密锁
	my_FtkeyClose = dlsym(handle, "Dongle_Close");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 读数据
	my_FtkeyReadData = dlsym(handle, "Dongle_ReadData");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 写数据
	my_FtkeyWriteData = dlsym(handle, "Dongle_WriteData");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	// 种子码
	my_FtkeySeed = dlsym(handle, "Dongle_Seed");
	if((error = dlerror()) != NULL)
	{
		fprintf(stderr, "%s\n", error);
		return UKEY_LIBRARY_LOAD_ERROR;
	}

	return NO_ERROR;
}

/*
 * ftkey_check_library, check if feitian key library is valid
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_check_library()
{
#if 0
	return (file_check_md5(FTKEY_SHARED_LIBRARY_PATH, FTKEY_SHARED_LIBRARY_MD5SUM)  &&
	        file_check_sha1(FTKEY_SHARED_LIBRARY_PATH, FTKEY_SHARED_LIBRARY_SHA1SUM) &&
		file_check_sha256(FTKEY_SHARED_LIBRARY_PATH, FTKEY_SHARED_LIBRARY_SHA256SUM));
#else
	return 1;
#endif
}

/*
 * ftkey_init, init feitian key environment
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_init()
{
	DWORD ret;
	DWORD count;

	ret = ftkey_load_library();
	if(ret != NO_ERROR)
		return ret;

	ret = ftkey_find(&count);
	if(ret != NO_ERROR)
		return ret;

	// delete vikey_login
	// delete my_VikeyGetType
	USER_SPACE = FEITIAN_USER_SPACE_SIZE;
	ADMIN_SPACE_OFFSET = FEITIAN_ADMIN_SPACE_OFFSET;

	return NO_ERROR;
}


/*
 * ftkey_activate_system, activate system with feitian key
 * return NO_ERROR if succeed, or error number if not
 * 将被外面调用的函数
 */
int ftkey_activate_system(const char* license_serial, 
			  const char* kyinfo_ukey, const char* expire_date, 
			  encrypted_number_generator ukey_raw_generator)
{
	DWORD ret = -1;
	char* ukey_serial = NULL;
	char* ukey_expire = NULL;
	char* ukey_expire_no_hyphen = NULL;
	char* raw = NULL;
	char* ukey_hid = NULL;
	int register_code_in_ukey = 0;
	//unsigned short is_zero = 1;
	//unsigned short can_reduce = 0;
	int offset = -1;
	int aligned_space = -1;
	int result = 0;
	char* register_code = NULL;
	int license_serial_mode = SERIAL_MODE_UNKNOWN;
	int ukey_serial_mode = SERIAL_MODE_UNKNOWN;
	char* ukey_produce = NULL;


	//printf("init\n");
	ret = ftkey_init();
	if(ret != NO_ERROR)
		return ret;

	aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);

	//printf("get data\n");
	ret = ftkey_get_data_force();
	if(ret != NO_ERROR)
		goto ftkey_activate_system_out;

	//printf("get license key file\n");
	ret = kylin_get_license(&ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);
	if(ret != NO_ERROR)
		goto ftkey_activate_system_out;

	//printf("get license serial\n");
	ukey_serial = kylin_get_license_serial(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_serial)
	{
		ret = UKEY_NO_SERIAL;
		goto ftkey_activate_system_out;
	}

	ukey_produce = kylin_get_license_produce(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);

	ukey_serial_mode = _serial_number_mode(ukey_serial);
	if((SERIAL_MODE_UNKNOWN == ukey_serial_mode) || (SERIAL_MODE_BATCH == ukey_serial_mode))
	{
		ret = UKEY_SERIAL_NUMBER_VALIDATION_ERROR;
		goto ftkey_activate_system_out;
	}
	if(SERIAL_MODE_LEGACY == ukey_serial_mode)
	{
		if(strcmp(license_serial, ukey_serial) != 0)
		{
			ret = SERIAL_NUMBER_MATCH_ERROR;
			goto ftkey_activate_system_out;
		}
	}

	license_serial_mode = _serial_number_mode(license_serial);
	if((SERIAL_MODE_LEGACY == license_serial_mode) || (SERIAL_MODE_NEW == license_serial_mode))
	{
		if(strcmp(license_serial, ukey_serial) != 0)
		{
			ret = SERIAL_NUMBER_MATCH_ERROR;
			goto ftkey_activate_system_out;
		}
	} else if(SERIAL_MODE_BATCH == license_serial_mode) {
		if(ukey_produce)
		{
			if(strcmp(license_serial, ukey_produce) != 0)
			{
				ret = PRODUCE_NUMBER_MATCH_ERROR;
				goto ftkey_activate_system_out;
			}
		}

		if(!_product_type_check(license_serial, ukey_serial))
		{
			ret = PRODUCT_TYPE_MATCH_ERROR;
			goto ftkey_activate_system_out;
		}
	} else {
		ret = LICENSE_SERIAL_NUMBER_VALIDATION_ERROR;
		goto ftkey_activate_system_out;
	}

	//printf("get expire time\n");
	ukey_expire = kylin_get_license_expire(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_expire)
	{
		ret = UKEY_NO_EXPIRE_TIME;
		goto ftkey_activate_system_out;
	}

	//printf("expire time remove hyphen\n");
	ukey_expire_no_hyphen = code_remove_hyphen(ukey_expire);
	if(!ukey_expire_no_hyphen)
	{
		ret = UKEY_EXPIRE_DEAL_HYPHEN_ERROR;
		goto ftkey_activate_system_out;
	}

	//printf("get ukey hid\n");
	ukey_hid = ftkey_get_hid();
	if(!ukey_hid)
	{
		ret = UKEY_GET_HID_ERROR;
		goto ftkey_activate_system_out;
	}

	//printf("generate ukey raw\n");
	if(!ukey_raw_generator)
	{
		ret = UKEY_ACTIVATE_SYSTEM_FAILED;
		goto ftkey_activate_system_out;
	}

	register_code = _kylin_activation_get_register_number(ukey_serial, &result);
	if(result != NO_ERROR)
	{
		ret = result;
		goto ftkey_activate_system_out;
	}
	if(!register_code)
	{
		ret = REGISTER_CREATION_ERROR;
		goto ftkey_activate_system_out;
	}

	raw = ukey_raw_generator(register_code, ukey_hid, ukey_expire_no_hyphen);
	if(!raw)
	{
		ret = UKEY_ACTIVATE_SYSTEM_FAILED;
		goto ftkey_activate_system_out;
	}


	if(!kyinfo_ukey && !expire_date)
		goto ukey_do_activation;

	if(!kyinfo_ukey && expire_date)
	{
		if(_same_expire_date(ukey_expire, expire_date))
		{
			ret = HAS_NORMALLY_ACTIVATED;
			goto ftkey_activate_system_out;
		}
	}

	if(kyinfo_ukey && expire_date)
	{
		if(_same_ukey_hid(ukey_hid, kyinfo_ukey))
		{
			goto ukey_do_activation;
		}
		else
		{
			if(_same_expire_date(ukey_expire, expire_date))
			{
				ret = HAS_ACTIVATED_WITH_ANOTHER_UKEY;
				goto ftkey_activate_system_out;
			}
			else
				goto ukey_do_activation;
		}
	}

ukey_do_activation:
	//printf("find register code\n");
	register_code_in_ukey = kylin_find_register_code(register_code, (const char*)ftkey_data, USER_SPACE);
	if(register_code_in_ukey)
	{
		ret = _kylin_activation_activate_system(raw, ukey_hid, ukey_serial, 0);
		goto ftkey_activate_system_out;
	}

	//printf("check module\n");
	// delete vikey_check_module

	//printf("is_zero\n");
	/*
	if(is_zero)
	{
		ret = UKEY_MODULE_IS_ZERO;
		goto ftkey_activate_system_out;
	}

	//printf("can_reduce\n");
	if(!can_reduce)
	{
		ret = UKEY_MODULE_CANNOT_REDUCE;
		goto ftkey_activate_system_out;
	}
	*/

	if(ftkey_space_is_full((const char*)ftkey_data, USER_SPACE))
	{
		ret = UKEY_SPACE_FULL;
		goto ftkey_activate_system_out;
	}

	//printf("activate system test\n");
	ret = _kylin_activation_activate_system_test(raw, ukey_hid, ukey_serial);
	if(ret != NO_ERROR)
		goto ftkey_activate_system_out;

    /*
     * do not decrease module here
     */
    /*
	//printf("decrease module\n");
	ret = vikey_decrease_module();
	if(ret != NO_ERROR)
		goto ftkey_activate_system_out;
    */

        /* do seed here*/
	ret = ftkey_seed();
	if(ret != NO_ERROR) {
		goto ftkey_activate_system_out;
	}

	//printf("activate system\n");
	ret = _kylin_activation_activate_system(raw, ukey_hid, ukey_serial, 0);
	if(ret != NO_ERROR)
		goto ftkey_activate_system_out;

	//printf("add register code\n");
	ret = ftkey_add_register_code(register_code, &offset);
	if(ret != NO_ERROR)
		goto ftkey_activate_system_out;

    // decrease module here
	//printf("decrease module\n");
	// delete vikey_decrease_module

	//printf("sync data\n");
	if((-1 != offset) && (0 == (offset % ENCRYPTED_NUMBER_LENGTH)) && (offset < aligned_space))
		memcpy(&ftkey_data[offset], register_code, ENCRYPTED_NUMBER_LENGTH);
	else
		ftkey_get_data_force();

	ret = NO_ERROR;
ftkey_activate_system_out:
	if(register_code)
		free(register_code);
	if(ukey_serial)
		free(ukey_serial);
	if(ukey_expire)
		free(ukey_expire);
	if(ukey_expire_no_hyphen)
		free(ukey_expire_no_hyphen);
	if(ukey_produce)
		free(ukey_produce);
	if(raw)
		free(raw);
	if(ukey_hid)
		free(ukey_hid);

	// delete vikey_logoff
	return ret;
}

/*
 * ftkey_dump_basic_info, dump vikey basic information to file f
 * return NO_ERROR if succeed, or error number if not
 */
static int ftkey_dump_basic_info(FILE *f, int display)
{
	int ret = -1;
	char* ukey_hid = NULL;
	char* ukey_serial = NULL;
	char* ukey_expire = NULL;
	char* ukey_to = NULL;
	int module_count = 0;
    int used_count = 0;
    int free_count = 0;
	char* ukey_produce = NULL;

	char line[MAX_LENGTH];
	char message[MAX_LENGTH];


	ukey_hid = ftkey_get_hid();
	if(!ukey_hid)
	{
		ret = UKEY_GET_HID_ERROR;
		goto ftkey_dump_base_out;
	}

	ukey_serial = kylin_get_license_serial(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_serial)
	{
		ret = UKEY_NO_SERIAL;
		goto ftkey_dump_base_out;
	}

	ukey_expire = kylin_get_license_expire(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);
	if(!ukey_expire)
	{
		ret = UKEY_NO_EXPIRE_TIME;
		goto ftkey_dump_base_out;
	}

	// delete vikey_get_module

	ukey_to = kylin_get_license_to(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);

	ukey_produce = kylin_get_license_produce(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);

    module_count = kylin_get_license_count(ftkey_license_keyfile, (const char*)&ftkey_data[ADMIN_SPACE_OFFSET]);

    if (module_count > 0)
    {
        used_count = _ftkey_used_register_count(module_count * ENCRYPTED_NUMBER_LENGTH);
        if (used_count == 0)
        {
            free_count = module_count;
        } else if (used_count > 0) {
            if (module_count > used_count)
            {
                free_count = module_count - used_count;
            }
        }
    } else {
        free_count = 0;
    }

	memset(line, 0, MAX_LENGTH);
	memset(message, 0, MAX_LENGTH);


	sprintf(line, "ukeyid=%s\nserial=%s\nterm=%s\nmodule_count=%d\nused_count=%d\nfree_count=%d\n", 
		ukey_hid, ukey_serial, ukey_expire, module_count, used_count, free_count);

	strcat(message, line);

	if(ukey_to)
	{
		memset(line, 0, MAX_LENGTH);
		sprintf(line, "to=%s\n", ukey_to);
		strcat(message, line);
	}

	if(ukey_produce)
	{
		memset(line, 0, MAX_LENGTH);
		sprintf(line, "produce=%s\n", ukey_produce);
		strcat(message, line);
	}

	strcat(message, "\n");

	if(display)
		fprintf(f, "%s", message);

	strcat(dump_message, message);

	ret = NO_ERROR;

ftkey_dump_base_out:
	if(ukey_hid)
		free(ukey_hid);
	if(ukey_serial)
		free(ukey_serial);
	if(ukey_expire)
		free(ukey_expire);
	if(ukey_to)
		free(ukey_to);
	if(ukey_produce)
		free(ukey_produce);

	return ret;
}

/*
 * ftkey_dump_register code, dump feitian key register code to file f
 * return NO_ERROR if succeed, or error number if not
 */
static void ftkey_dump_register_code(FILE *f, int display)
{
	int _index = 0;
	int count = 0;
	char buf[ ENCRYPTED_NUMBER_LENGTH + 1];
	char message[MAX_LENGTH];
	char line[LINE_SIZE];

	memset(buf, 0, ENCRYPTED_NUMBER_LENGTH + 1);
	memset(message, 0, MAX_LENGTH);
	memset(line, 0, LINE_SIZE);

	count = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);

	for(_index = 0; _index < count; _index += ENCRYPTED_NUMBER_LENGTH)
	{
		memcpy(buf, &ftkey_data[_index], ENCRYPTED_NUMBER_LENGTH);
		if(register_code_is_valid(buf))
		{
			sprintf(line, "%s\n", buf);
			strcat(message, line);
		}
	}

	if(display)
		fprintf(f, "%s", message);

	strcat(dump_message, message);
}

/*
 * _ftkey_used_register_count, return the number of register code which has been stored.
 */
static int _ftkey_used_register_count(int space_size)
{
    int _index = 0;
    int count = 0;
    int space = 0;
    int aligned_space = 0;
    char buf[ENCRYPTED_NUMBER_LENGTH + 1] = {0};

    memset(buf, 0, ENCRYPTED_NUMBER_LENGTH + 1);

    aligned_space = USER_SPACE - (USER_SPACE % SPACE_ALIGNMENT);
    if (space_size < aligned_space)
    {
        space = space_size;
    } else {
        space = aligned_space;
    }

    for (_index = 0; _index < space; _index += ENCRYPTED_NUMBER_LENGTH)
    {
        memcpy(buf, &ftkey_data[_index], ENCRYPTED_NUMBER_LENGTH);
        if (register_code_is_valid(buf))
        {
            count++;
        }
    }

    return count;
}

/*
 * _ftkey_dump, dump feitian key information to file f
 * encrypt: whether encryption is needed or not
 * display_all: whether display all information or not
 * return NO_ERROR if succeed, or error number if not
 */
static int _ftkey_dump(FILE* f, int encrypt, int display_all)
{
	DWORD ret = -1;

	int display_basic = 1;
	int display_register_code = 0;

	char* encrypted_message = NULL;
	unsigned long encrypted_message_size = -1;

	ret = ftkey_init();
	if(ret != NO_ERROR)
		return ret;

	ret = ftkey_get_data_force();
	if(ret != NO_ERROR)
		return ret;

	memset(dump_message, 0, sizeof(dump_message));

	if(display_all)
	{
		display_basic = 1;
		display_register_code = 1;
	}

	if(encrypt)
	{
		display_basic = 0;
		display_register_code = 0;
	}

	ret = ftkey_dump_basic_info(f, display_basic);
	if(ret != NO_ERROR)
		return ret;

	ftkey_dump_register_code(f, display_register_code);

	if(encrypt)
	{
		ret = gpg_encrypt(dump_message, &encrypted_message, &encrypted_message_size);
		if(ret != NO_ERROR)
			return ret;

		if(encrypted_message && (encrypted_message_size > 0))
		{
			fprintf(f, "%s", encrypted_message);
		}
	}

	return NO_ERROR;
}

/*
 * ftkey_dump, dump feitian key information to file f
 * encrypt: whether encryption is needed or not
 * display_all: whether display all information or not
 * return NO_ERROR if succeed, or error number if not
 */
int ftkey_dump(FILE* f, int encrypt, int display_all)
{
	return _ftkey_dump(f, encrypt, display_all);
}

#endif // ENABLE_FTKEY
